{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "import yaml\n",
    "import random\n",
    "from aip import AipSpeech\n",
    "root_dir = \"/xiaobai/\"\n",
    "sys.path.append(root_dir)\n",
    "import snowboydecoder\n",
    "class XiaoBai:\n",
    "    #初始化函数，设置关键字模型，\n",
    "    def __init__(self,keyword_model,callback=None):\n",
    "        self.detector = snowboydecoder.HotwordDetector(keyword_model, sensitivity=0.5)\n",
    "        self.skills = []\n",
    "        self.greetings = [\"嗯哼.mp3\",\"我在.mp3\",\"请说.mp3\"]\n",
    "        self.callback = callback\n",
    "        with open(root_dir+\"config.yaml\") as f:\n",
    "            config = yaml.load(f)['baidu_yuyin']\n",
    "            self.client = AipSpeech(config['app_id'], config['api_key'], config['secret_key'])\n",
    "    #检测到关键字后的操作\n",
    "    def _callback(self):\n",
    "            self.detector.terminate()\n",
    "            n = random.randint(0,len(self.greetings)-1)\n",
    "            notify_sound = root_dir+'resources/greetings/'+self.greetings[n]\n",
    "            os.system(\"mpg123 \"+notify_sound)   \n",
    "            if self.callback is None:\n",
    "                res = self.listen_and_recognize()\n",
    "                if res == \"\":\n",
    "                    self.speak(\"小白没听清呢\")\n",
    "                else:\n",
    "                    print(\"你：\"+res)\n",
    "                    handled = False\n",
    "                    for skill in self.skills:\n",
    "                        if skill.handle(res,callback=self.speak):\n",
    "                            handled = True\n",
    "                            break\n",
    "                    if not handled:\n",
    "                        self.speak(\"小白暂时不会处理呢\")\n",
    "            else:\n",
    "                self.callback()\n",
    "            self.detector.start(detected_callback=self._callback,sleep_time=0.03)\n",
    "    #添加技能\n",
    "    def add_skill(self,skill):\n",
    "        if skill.type == \"skill\":\n",
    "            self.skills.append(skill)\n",
    "    def listen_for_keyword(self):\n",
    "        try:\n",
    "            print('Listening...')\n",
    "            self.detector.start(detected_callback=self._callback,sleep_time=0.03)\n",
    "        except KeyboardInterrupt:\n",
    "            print('stop')\n",
    "        finally:\n",
    "            self.detector.terminate()            \n",
    "    #录音和识别函数,调用arecord录音\n",
    "    def listen_and_recognize(self,length = 4):\n",
    "        os.system(\"arecord -d %d -r 16000 -c 1 -t wav -f S16_LE record.wav\" % (length,) )    \n",
    "        with open(\"./record.wav\", 'rb') as fp:\n",
    "            res = self.client.asr(fp.read(), 'wav', 16000, { 'dev_pid': 1536,})\n",
    "            if isinstance(res, dict) and res['err_no']==0:\n",
    "                return res[\"result\"][0]\n",
    "            else:\n",
    "                #print(res)\n",
    "                return \"\"\n",
    "    #调用百度语音合成API进行回复\n",
    "    def speak(self,text = '你好呀',lang = 'zh',type = 1 , vol = 5, spd = 5 , pit = 5):\n",
    "        print('小白：'+text)\n",
    "        result  = self.client.synthesis(text, lang, type, {'vol': vol,'spd':spd,'pit':pit})\n",
    "        # 识别正确返回语音二进制 错误则返回dict\n",
    "        if not isinstance(result, dict):\n",
    "            with open('speak.mp3', 'wb') as f:\n",
    "                f.write(result)\n",
    "            os.system('mpg123 speak.mp3')\n",
    "        else:\n",
    "            print('emmmm，小白出错了呢',result)\n",
    "            \n",
    "import abc #利用abc模块实现抽象类\n",
    "#编写扩展技能的基本格式，has_intent函数检测是否有需要该技能处理的意图，action函数执行对应的处理\n",
    "class BaseSkill(metaclass=abc.ABCMeta):\n",
    "    type='skill'\n",
    "    #参数说明 \n",
    "    #    text：语音识别的到的文本\n",
    "    #    callback：反馈文本的处理函数，默认直接打印，也可以传入语音合成函数进行语音回复\n",
    "    #定义抽象方法，检测是否有需要该技能处理的意图\n",
    "    @abc.abstractmethod \n",
    "    def has_intent(self,text=\"\"):\n",
    "        pass\n",
    "    #定义抽象方法，根据意图处理处理信息\n",
    "    @abc.abstractmethod\n",
    "    def action(self,text=\"\"):\n",
    "        pass\n",
    "    #处理函数，根据意图处理处理信息，返回是否继续检测意图\n",
    "    def handle(self,text=\"\",callback=print):\n",
    "        if self.has_intent(text=text):\n",
    "            self.action(text=text,callback=callback)\n",
    "            return True\n",
    "        else:\n",
    "            return False   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请问尊名～\n",
      "北京:周二,多云 南风微风,最低气温16度，最高气温30度\n",
      "春节爸妈炒菜时争论鱿鱼的切法，争得不可开交时老爸大吼“切成平行四边形！！！”站在一边的我的弟弟都被震撼了！不愧是70年代的高材生啊！老妈彻底无语凌乱中！\n"
     ]
    }
   ],
   "source": [
    "import requests\n",
    "import json\n",
    "import yaml\n",
    "\n",
    "class TalkSkill(BaseSkill):\n",
    "    def __init__(self):\n",
    "        root_dir = \"/xiaobai/\"\n",
    "        with open(root_dir+\"config.yaml\") as f:\n",
    "            config = yaml.load(f)['tuling']\n",
    "            self.key = config['key']\n",
    "            self.url = 'http://www.tuling123.com/openapi/api'\n",
    "    #继承BaseSkill类，必须定义has_intent和action方法\n",
    "    def has_intent(self,text=\"\"):\n",
    "        if text!= \"\":\n",
    "            return True\n",
    "        return False\n",
    "    def action(self,text=\"\",callback=print):\n",
    "        try:\n",
    "            req = {'key':self.key,'info':text}\n",
    "            res = requests.post(url = self.url, data = req)\n",
    "            #print(res.text)\n",
    "            jd = json.loads(res.text)\n",
    "            callback(jd['text'])\n",
    "        except:\n",
    "            callback(\"出错了呢，可能网络不太好\")\n",
    "if __name__ == '__main__':\n",
    "    s = TalkSkill()\n",
    "    s.handle(\"你好呀\")\n",
    "    s.handle(\"北京明天天气怎么样\")\n",
    "    s.handle(\"讲个冷笑话\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Listening...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:snowboy:Keyword 1 detected at time: 2019-05-13 15:00:51\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你：你好呀\n",
      "小白：哈喽！\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:snowboy:Keyword 1 detected at time: 2019-05-13 15:01:04\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你：两个黄鹂鸣翠柳\n",
      "小白：两岸猿声啼不住～\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:snowboy:Keyword 1 detected at time: 2019-05-13 15:01:26\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你：两个黄鹂鸣翠柳的下一句\n",
      "小白：一行白鹭上青天\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:snowboy:Keyword 1 detected at time: 2019-05-13 15:01:40\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你：北京明天天气怎么样\n",
      "小白：北京:周二,多云 南风微风,最低气温16度，最高气温30度\n",
      "stop\n"
     ]
    }
   ],
   "source": [
    "keyword_model = root_dir+'resources/小白.pmdl'\n",
    "xiaobai = XiaoBai(keyword_model=keyword_model)\n",
    "xiaobai.add_skill(TalkSkill())\n",
    "xiaobai.listen_for_keyword()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
